package com.mike.bdb.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mike.bdb.manager.DBManager;
import com.mike.bdb.util.DPLBDBNameUtil.KeyName;
import com.sleepycat.persist.model.Entity;
import com.sleepycat.persist.model.PrimaryKey;
import com.sleepycat.persist.model.SecondaryKey;

/**
 * 反射工具
 * @author <a href="zhaoming23@gmail.com">Mike</a>
 * @version 1.0
 * 2011-4-21 上午09:30:50
 */
public class ReflectUtil {
	
	private static final Logger LOG = LoggerFactory.getLogger(ReflectUtil.class);

	public static Object getRealType(Class clazz, String value) {
		String string = clazz.toString();
		if(LOG.isDebugEnabled()) {
			LOG.debug(" >> " + string);
		}
		return getRealType(string, value);
	}
	
	public static Object getRealType(String string, String value) {
		if(null == string || "".equals(string.trim())) return null;
		if(string.endsWith("java.lang.String")) {
			return value;
		} else if (string.endsWith("java.lang.Integer")
				|| string.endsWith("int")) {
			return new Integer(value);
		} else if (string.endsWith("java.lang.Boolean")
				|| string.endsWith("boolean")) {
			return new Boolean(value);
		} else if (string.endsWith("java.lang.Short")
				|| string.endsWith("short")) {
			return new Short(value);
		} else if (string.endsWith("java.lang.Long")
				|| string.endsWith("long")) {
			return new Long(value);
		} else if (string.endsWith("java.lang.Float")
				|| string.endsWith("float")) {
			return new Float(value);
		} else if (string.endsWith("java.lang.Double")
				|| string.endsWith("double")) {
			return new Double(value);
		} else if (string.endsWith("java.lang.Character")
				|| string.endsWith("char")) {
			return new Character(value.charAt(0));
		} else if (string.endsWith("java.lang.Byte")
				|| string.endsWith("byte")) {
			return new Byte(value);
		}
		
		return (Object) value;
	}
	
	/** 获得模型类的主键类型 */
	public static Class getPKClass(Class<?> clazz) {
		if(null == clazz) return null;
		if(!clazz.isAnnotationPresent(Entity.class)) return null;
		Type pk = null;
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			boolean annotationPresent = field.isAnnotationPresent(PrimaryKey.class);
			if(annotationPresent) {
				pk = field.getType();
				break;
			}
		}
		if(null == pk) throw new RuntimeException(clazz + " no PrimaryKey annotation.");
		
		String string = pk.toString();
		
		if(string.endsWith("java.lang.String")) {
			return String.class;
		} else if (string.endsWith("java.lang.Integer")
				|| string.endsWith("int")) {
			return Integer.class;
		} else if (string.endsWith("java.lang.Boolean")
				|| string.endsWith("boolean")) {
			return Boolean.class;
		} else if (string.endsWith("java.lang.Short")
				|| string.endsWith("short")) {
			return Short.class;
		} else if (string.endsWith("java.lang.Long")
				|| string.endsWith("long")) {
			return Long.class;
		} else if (string.endsWith("java.lang.Float")
				|| string.endsWith("float")) {
			return Float.class;
		} else if (string.endsWith("java.lang.Double")
				|| string.endsWith("double")) {
			return Double.class;
		} else if (string.endsWith("java.lang.Character")
				|| string.endsWith("char")) {
			return Character.class;
		} else if (string.endsWith("java.lang.Byte")
				|| string.endsWith("byte")) {
			return Byte.class;
		}
		
		return (Class) pk.getClass();
	}
	
	public static Class getSKClass(Class<?> clazz, String fieldName) {
		if(null == clazz) return null;
		if(!clazz.isAnnotationPresent(Entity.class)) return null;
		Map<String, Type> sks = new HashMap<String, Type>();
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			boolean annotationPresent = field.isAnnotationPresent(SecondaryKey.class);
			if(annotationPresent) {
				sks.put(field.getName(), field.getType());
			}
		}
		if(0 == sks.size()) throw new RuntimeException(clazz + " no SecondaryKey annotation.");
		
		Type type = sks.get(fieldName);
		
		if(null == type) return null;
		
		String string = type.toString();
		
		if(string.endsWith("java.lang.String")) {
			return String.class;
		} else if (string.endsWith("java.lang.Integer")
				|| string.endsWith("int")) {
			return Integer.class;
		} else if (string.endsWith("java.lang.Boolean")
				|| string.endsWith("boolean")) {
			return Boolean.class;
		} else if (string.endsWith("java.lang.Short")
				|| string.endsWith("short")) {
			return Short.class;
		} else if (string.endsWith("java.lang.Long")
				|| string.endsWith("long")) {
			return Long.class;
		} else if (string.endsWith("java.lang.Float")
				|| string.endsWith("float")) {
			return Float.class;
		} else if (string.endsWith("java.lang.Double")
				|| string.endsWith("double")) {
			return Double.class;
		} else if (string.endsWith("java.lang.Character")
				|| string.endsWith("char")) {
			return Character.class;
		} else if (string.endsWith("java.lang.Byte")
				|| string.endsWith("byte")) {
			return Byte.class;
		}
		return (Class) type.getClass();
	}
	
	public static Class getFieldClass(Class<?> clazz, String fieldName) {
		if(null == clazz) return null;
		if(!clazz.isAnnotationPresent(Entity.class)) return null;
		Map<String, Type> sks = new HashMap<String, Type>();
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			sks.put(field.getName(), field.getType());
		}
		if(0 == sks.size()) throw new RuntimeException(clazz + " no SecondaryKey annotation.");
		
		Type type = sks.get(fieldName);
		
		if(null == type) return null;
		
		String string = type.toString();
		
		if(string.endsWith("java.lang.String")) {
			return String.class;
		} else if (string.endsWith("java.lang.Integer")
				|| string.endsWith("int")) {
			return Integer.class;
		} else if (string.endsWith("java.lang.Boolean")
				|| string.endsWith("boolean")) {
			return Boolean.class;
		} else if (string.endsWith("java.lang.Short")
				|| string.endsWith("short")) {
			return Short.class;
		} else if (string.endsWith("java.lang.Long")
				|| string.endsWith("long")) {
			return Long.class;
		} else if (string.endsWith("java.lang.Float")
				|| string.endsWith("float")) {
			return Float.class;
		} else if (string.endsWith("java.lang.Double")
				|| string.endsWith("double")) {
			return Double.class;
		} else if (string.endsWith("java.lang.Character")
				|| string.endsWith("char")) {
			return Character.class;
		} else if (string.endsWith("java.lang.Byte")
				|| string.endsWith("byte")) {
			return Byte.class;
		}
		return (Class) type.getClass();
	}
	
	public static void invokeSet(String curStoreClazz, String property, Object element, Object value, HashMap<String, Type> types, boolean isPutnew) {
		Class<Object> classBy = ReflectUtil.getClassBy(curStoreClazz);
		for(Method m:classBy.getMethods()) {
			if(!m.getName().startsWith(property.replace("get", "set"))) continue;
			
			Type type = types.get(property);
			String typeName = type.toString();
			
			invoke(m, typeName, element, value);
			
		}
		//TODO ★ Berkeley DB put 
		Class pkClass = ReflectUtil.getPKClass(classBy);
		if(isPutnew) {
			DBManager.getInstance().getPKStore(pkClass, classBy).put(element);
		}
	}
	
	public static void invoke(Method m, String t, Object element, Object value) {
		if(null == m) return;
		if(null == t) return;
		if(null == element) return;
		if(null == value) return;
		try {
			Type genericReturnType = m.getGenericReturnType();
			if(t.endsWith("int") || t.endsWith("java.lang.Integer")) {
				m.invoke(element, Integer.parseInt(String.valueOf(value)));
				return;
			} else if (t.endsWith("byte") || t.endsWith("java.lang.Byte")){
				m.invoke(element, Byte.parseByte(String.valueOf(value)));
				return;
			} else if (t.endsWith("short") || t.endsWith("java.lang.Short")){
				m.invoke(element, Short.parseShort(String.valueOf(value)));
				return;
			} else if (t.endsWith("long") || t.endsWith("java.lang.Long")){
				m.invoke(element, Long.parseLong(String.valueOf(value)));
				return;
			} else if (t.endsWith("boolean") || t.endsWith("java.lang.Boolean")){
				m.invoke(element, Boolean.parseBoolean(String.valueOf(value)));
				return;
			} else if (t.endsWith("float") || t.endsWith("java.lang.Float")){
				m.invoke(element, Float.parseFloat(String.valueOf(value)));
				return;
			} else if (t.endsWith("double") || t.endsWith("java.lang.Double")){
				m.invoke(element, Double.parseDouble(String.valueOf(value)));
				return;
			} else {
				//other...
				m.invoke(element, value);
				return;
			}
		} catch (Exception e) {
			//ignore
		}
	}

	public static void invokeSet(String curStoreClazz, String property, Object element, Object value, HashMap<String, Type> types) {
		invokeSet(curStoreClazz, property, element, value, types, true);
	}
	
	public static boolean isSecondaryField(String curStoreClazz, int arg1, Object element) {
		boolean result = false;
		Class<Object> classBy = ReflectUtil.getClassBy(curStoreClazz);
		List<Method> berkeleyAttribute = ReflectUtil.getBerkeleyAttribute(classBy);
		int index = 0;
		for(Method m : berkeleyAttribute) {
			if(index == arg1) {
				try {
					String name = m.getName();
					
					name = name.replace("get", "");
					
					name = name.substring(0, 1).toLowerCase() + name.substring(1, name.length());
					
					Field field = element.getClass().getDeclaredField(name);
					
					result = field.isAnnotationPresent(SecondaryKey.class);
				} catch (Exception e) {
					//eat it.
				}
				return result;
			}
			index++;
		}
		return result;
	}
	
	public static String invokeGet(String curStoreClazz, int arg1, Object element) {
		String result = null;
		Class<Object> classBy = ReflectUtil.getClassBy(curStoreClazz);
		List<Method> berkeleyAttribute = ReflectUtil.getBerkeleyAttribute(classBy);
		int index = 0;
		String temp = "";
		for(Method m:berkeleyAttribute) {
			if(index == arg1) {
				try {
					temp = String.valueOf(m.invoke(element, null));
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
				return temp;
			}
			index++;
		}
		return result;
	}
	
	public static String invokeGet(String curStoreClazz, String property, Object element) {
		String result = null;
		Class<Object> classBy = ReflectUtil.getClassBy(curStoreClazz);
		for(Method m:classBy.getMethods()) {
			if(!m.getName().equals(property)) continue;
			try {
				return String.valueOf(m.invoke(element, null));
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		return result;
	}
	
	/**
	 * 根据storeClassName 获得其主键field
	 * @param storeClassName
	 * @return
	 */
	public static Field getPKFieldByStoreClassName(String storeClassName) {
		Field pkField = null;
		if(null == storeClassName || "".equals(storeClassName.trim())) return pkField;
		Class<Object> classBy = ReflectUtil.getClassBy(storeClassName);
		if(null == classBy) return pkField;
		pkField = ReflectUtil.getPKField(classBy);
		return pkField;
	}
	
	public static Method getMethod(Class<?> storeClass, String name) {
		Method nameMethod = null;
		List<Method> berkeleyAttribute = ReflectUtil.getBerkeleyAttribute(storeClass);
		for (Method method : berkeleyAttribute) {
			if(method.getName().endsWith(name)) {
				nameMethod = method;
				break;
			}
		}
		return nameMethod;
	}
	
	/**
	 * 获得主键的设置方法
	 * @param storeClass
	 * @return setMethod
	 */
	public static Method getPKGetMethod(Class<?> storeClass) {
		Method pkMethod = null;
		if(null == storeClass) return pkMethod;
		Field pkField = getPKField(storeClass);
		if(null == pkField) return pkMethod;
		return getFieldGetMethod(storeClass, pkField);
	}
	
	/**
	 * 获得主键的设置方法
	 * @param storeClass
	 * @return setMethod
	 */
	public static Method getPKSetMethod(Class<?> storeClass) {
		Method pkMethod = null;
		if(null == storeClass) return pkMethod;
		Field pkField = getPKField(storeClass);
		if(null == pkField) return pkMethod;
		return getFieldSetMethod(storeClass, pkField);
	}
	
	/**
	 * 按照主键类型为String的，获得方法对象描述
	 * @param storeClass storeClass
	 * @param field 主键属性
	 * @return setMethod
	 */
	public static Method getFieldSetMethod(Class<?> storeClass, Field field) {
		Method setMethod = null;
		if(null == field || null == storeClass) return setMethod;
		String fieldName = field.getName();
		String prefixChar = fieldName.substring(0, 1);
		String bingo = "set" + prefixChar.toUpperCase() + fieldName.substring(1, fieldName.length());
		try {
			setMethod = storeClass.getMethod(bingo, String.class);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return setMethod;
	}
	
	public static Method getFieldGetMethod(Class<?> storeClass, Field field) {
		Method setMethod = null;
		if(null == field || null == storeClass) return setMethod;
		String fieldName = field.getName();
		String prefixChar = fieldName.substring(0, 1);
		String bingo = "get" + prefixChar.toUpperCase() + fieldName.substring(1, fieldName.length());
		try {
			setMethod = storeClass.getMethod(bingo, null);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return setMethod;
	}
	
	/***
	 * 根据Class 获得Pk Field
	 * @param storeClass
	 * @return
	 */
	public static Field getPKField(Class<?> storeClass) {
		Field pk = null;
		if(null == storeClass) return pk;
		Field[] fields = storeClass.getDeclaredFields();
		for (Field field : fields) {
			if(field.isAnnotationPresent(com.sleepycat.persist.model.PrimaryKey.class)) {
				pk = field;
				break;
			}
		}
		return pk;
	}
	
	/***
	 * 根据Class 获得sk Field 的 Method Name
	 * @param storeClass
	 * @return
	 */
	public static Set<String> getSKFieldMethodNames(Class<?> storeClass) {
		if(null == storeClass) return null;
		Set<String> result = new HashSet<String>();
		Field[] fields = storeClass.getDeclaredFields();
		boolean isHas = false;
		for (Field field : fields) {
			if(field.isAnnotationPresent(com.sleepycat.persist.model.SecondaryKey.class)) {
				Method skMethod = getFieldGetMethod(storeClass, field);
				result.add(skMethod.getName());
				isHas = true;
			}
		}
		return isHas ? result : null;
	}
	
	/**
	 * 根据KeyName 获得Class
	 * @param keyName
	 * @return
	 */
	public static Class<Object> getClassBy(KeyName keyName) {
		if(null == keyName) return null;
		return getClassBy(keyName.getCurStoreClazz());
	}
	
	/**
	 * 根据className 获得Class
	 * @param name className
	 * @return
	 */
	public static Class<Object> getClassBy(String name) {
		Class<Object> storeClazz = null;
		if(null == name || "".equals(name.trim())) return storeClazz;
		try {
			storeClazz = (Class<Object>) Class.forName(name);
		} catch (ClassNotFoundException e) {
			if(LOG.isDebugEnabled()) {
				LOG.debug("Class not find [" + name + "] " + e.getMessage());
			}
		}
		return storeClazz;
	}
	
	/**
	 * 这些属性是自己定义的不包含Object的一些基本方法
	 * @param clazz	Berkeley DB Class which annotation by {@link com.sleepycat.persist.model.Entity}
	 * @return 自定义属性Set
	 */
	public static List<Method> getBerkeleyAttribute(Class<?> clazz) {
		if(null == clazz) return null;
		List<Method> temp = new ArrayList<Method>();
		List<Method> skArr = new ArrayList<Method>();
		List<Method> result = new ArrayList<Method>();
		Method[] methods = clazz.getMethods();
		
		//主键
		Field pk = ReflectUtil.getPKField(clazz);
		//次级键
		Set<String> sks = ReflectUtil.getSKFieldMethodNames(clazz);
		
		for (Method method : methods) {
			if(!method.getName().startsWith("get") 
					|| method.getName().startsWith("getClass")) {
				continue;
			}
			Method pkMethod = getFieldGetMethod(clazz, pk);
			if(pkMethod.getName().equals(method.getName())) {
				result.add(method);
			} else if(null != sks && sks.contains(method.getName())) {
				skArr.add(method);
			} else {
				temp.add(method);
			}
		}
		result.addAll(skArr);
		result.addAll(temp);
		return result;
	}
	
	public static List<Method> getBerkeleyQueryPKAttribute(Class<?> clazz) {
		if(null == clazz) return null;
		List<Method> result = new ArrayList<Method>();
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			if(!method.getName().startsWith("get") 
					|| method.getName().startsWith("getClass")) {
				continue;
			}
			String name = method.getName();
			
			name = name.replace("get", "");
			
			name = name.substring(0, 1).toLowerCase() + name.substring(1, name.length());
			
			Field field = ReflectUtil.getPKField(clazz);
			Method fieldGetMethod = getFieldGetMethod(clazz, field);
			if(fieldGetMethod.getName().equals(method.getName())) {
				//主键
				result.add(method);
			}
		}
		return result;
	}
	public static List<Method> getBerkeleyQueryAttribute(Class<?> clazz) {
		if(null == clazz) return null;
		List<Method> temp = new ArrayList<Method>();
		List<Method> result = new ArrayList<Method>();
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			if(!method.getName().startsWith("get") 
					|| method.getName().startsWith("getClass")) {
				continue;
			}
			String name = method.getName();
			
			name = name.replace("get", "");
			
			name = name.substring(0, 1).toLowerCase() + name.substring(1, name.length());
			
			Field field = ReflectUtil.getPKField(clazz);
			Method fieldGetMethod = getFieldGetMethod(clazz, field);
			if(fieldGetMethod.getName().equals(method.getName())) {
				//主键
				//result.add(method);
			} else {
				try {
					Field declaredField = clazz.getDeclaredField(name);
					if(null == declaredField
							|| !declaredField.isAnnotationPresent(SecondaryKey.class)) {
						continue;
					}
				} catch (Exception e) {
					continue;
					//eat it.
				}
				temp.add(method);
			}
		}
		result.addAll(temp);
		return result;
	}

	public static HashMap<String, java.lang.reflect.Type> getMapByListMethod(List<Method> allMethod) {
		HashMap<String, Type> methodReturnMap = new HashMap<String, Type>();
		for (Method method : allMethod) {
			methodReturnMap.put(method.getName(), method.getGenericReturnType());
		}
		return methodReturnMap;
	}
}
